package com.lordjoe.collectiveintelligence.svm;

import libsvm.*;

import java.io.*;
import java.util.*;

import com.lordjoe.collectiveintelligence.svm.matchmaker.*;

/**
 * com.lordjoe.collectiveintelligence.svm.SVMUtilities
 *
 * @author Steve Lewis
 * @date May 20, 2009
 */
public class SVMUtilities
{
    public static SVMUtilities[] EMPTY_ARRAY = {};
    public static Class THIS_CLASS = SVMUtilities.class;
    public static final Random RND = new Random();


    public static ISVMDataSource[] readTrainingAndModelSets(String fileBase)
    {
        String trainingName = fileBase + ".training";
        File training = new File(trainingName);
        if(!training.exists())   {
            return buildTrainingSet(fileBase);
        }
        ISVMDataSource[] ret = new ISVMDataSource[2];
        ret[0] = readSVMLibSet(trainingName, DataSetType.Training);
    //   System.out.println("Read Training Set");
        ret[1] = readSVMLibSet(fileBase + ".model", DataSetType.Production);
     //   System.out.println("Read Data Set");
        return ret;
    }

    public static ISVMDataSource[] buildTrainingSet(String fileBase)
      {
          String trainingName = fileBase + ".raw";
          ISVMDataSource source = readSVMLibSet(trainingName, DataSetType.Raw);
          ISVMDataSource[] ret = buildTrainingAndProductionSets( source, source.getDataItems().length/ 2);

          return ret;
      }

    public static ISVMDataSource readSVMLibSet(String fileName, DataSetType type)
    {
        SVMDataSource ret = new SVMDataSource(type);
        try {
            LineNumberReader rdr = new LineNumberReader(new FileReader(fileName));
            String line = rdr.readLine();
            while (line != null) {
                ret.addDataItem(buildDataItem(line));
                line = rdr.readLine();
            }
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
        return ret;
    }

    public static ISVMDataItem buildDataItem(String line)
    {
         return new SVMLibDataItem(line);
    }


    /**
     * choose a training set from raw data
     * remaining items are production  this version
     * is repeatable
     *
     * @param source          - sourc data
     * @param trainingSetSize size of training set
     * @return elt 0 is training set, 1 is production
     */
    public static ISVMDataSource[] buildTrainingAndProductionSets(ISVMDataSource source,
                                                                  int trainingSetSize)
    {
        ISVMDataSource[] ret = new ISVMDataSource[2];
        SVMDataSource trainingSet = new SVMDataSource(DataSetType.Training);
        ret[0] = trainingSet;
        SVMDataSource productionSet = new SVMDataSource(DataSetType.Production);
        ret[1] = productionSet;
        ISVMDataItem[] items = source.getDataItems();
        if (trainingSetSize > items.length)
            throw new IllegalArgumentException("Training set cannot be bigger than source");
        ISVMDataItem[] isvmDataItems = source.getDataItems();
        // first items are the training set
        for (int i = 0; i <trainingSetSize; i++) {
            ISVMDataItem item = isvmDataItems[i];
             trainingSet.addDataItem(item);
        }
        // rest items are the production set
         for (int i = trainingSetSize; i < isvmDataItems.length; i++) {
            ISVMDataItem item = isvmDataItems[i];
            productionSet.addDataItem(item);

        }

        return ret;

    }  /**
     * choose a training set from raw data
     * remaining items are production this version
     * is random
     * @param source          - sourc data
     * @param trainingSetSize size of training set
     * @return elt 0 is training set, 1 is production
     */
    public static ISVMDataSource[] buildRandomTrainingAndProductionSets(ISVMDataSource source,
                                                                  int trainingSetSize)
    {
        ISVMDataSource[] ret = new ISVMDataSource[2];
        SVMDataSource trainingSet = new SVMDataSource(DataSetType.Training);
        ret[0] = trainingSet;
        SVMDataSource productionSet = new SVMDataSource(DataSetType.Production);
        ret[1] = productionSet;
        ISVMDataItem[] items = source.getDataItems();
        if (trainingSetSize > items.length)
            throw new IllegalArgumentException("Training set cannot be bigger than source");
        Set<ISVMDataItem> production = new HashSet<ISVMDataItem>(Arrays.asList(items));
        Set<ISVMDataItem> training = new HashSet<ISVMDataItem>();

        while (training.size() < trainingSetSize) {
            items = chooseItem(items, production, training);
        }
        for (ISVMDataItem item : training)
            trainingSet.addDataItem(item);
        for (ISVMDataItem item : production)
            productionSet.addDataItem(item);
        return ret;

    }

    protected static ISVMDataItem[] chooseItem(ISVMDataItem[] items, Set<ISVMDataItem> production,
                                               Set<ISVMDataItem> trainingSet)
    {
        int length = items.length;
        if(length <= 0)
            throw new UnsupportedOperationException("Fix This"); // ToDo
        int chosen = RND.nextInt(length);
        ISVMDataItem chosenItem = items[chosen];
        if (trainingSet.contains(chosenItem)) {  // already chosen
            // rebuild the choice set
            items = production.toArray(ISVMDataItem.EMPTY_ARRAY);
            return chooseItem(items, production, trainingSet);
        }
        else {
            trainingSet.add(chosenItem);
            production.remove(chosenItem);
            return items;
        }
    }


//    public static svm_problem buildSVMProblem(MatchMaker mm)
//    {
//        return buildSVMProblem(new mm, mm.getPotentialPairs());
//    }

    private static int INDEX_COUNTER = 0;
    public static final int DISTANCE_INDEX = INDEX_COUNTER++;
    public static final int MALE_AGE_INDEX = INDEX_COUNTER++;
    public static final int FEMALE_AGE_INDEX = INDEX_COUNTER++;
    public static final int INTERESTS_INDEX = INDEX_COUNTER++;

    public static final int NUMBER_MATCHMAKER_VARIABLES = INDEX_COUNTER;

    public static svm_problem buildSVMProblem(ISVMDataSource mm)
    {
        return buildSVMProblem(mm, mm.getDataItems());
    }

    public static svm_problem buildSVMProblem(ISVMDataSource mm, ISVMDataItem[] pairs)
    {

        svm_problem prob = new svm_problem();
        prob.l = pairs.length;
        prob.x = new svm_node[prob.l][];
        for (int i = 0; i < pairs.length; i++) {
            ISVMDataItem pair = pairs[i];
            svm_node[] nodes = pair.getNodes();
            prob.x[i] = nodes;
        }
        prob.y = new double[prob.l];
        for (int i = 0; i < pairs.length; i++) {
            ISVMDataItem pair = pairs[i];
            prob.y[i] = pair.getClassification();
        }

        return prob;
    }


    public static int booleanTo01(boolean b)
    {
        if (b)
            return 1;
        else
            return 0;
    }

    public static double booleanToFuzzy01(boolean b)
    {
        if (b)
            return 1 + 0.1 * RND.nextGaussian();
        else
            return 0 + 0.1 * RND.nextGaussian();
    }


    /*
    	default_parameters = {
	'svm_type' : C_SVC,
	'kernel_type' : RBF,
	'degree' : 3,
	'gamma' : 0,		# 1/k
	'coef0' : 0,
	'nu' : 0.5,
	'cache_size' : 40,
	'C' : 1,
	'eps' : 1e-3,
	'p' : 0.1,
	'shrinking' : 1,
	'nr_weight' : 0,
	'weight_label' : [],
	'weight' : [],
	'probability' : 0
     */
    public static svm_parameter buildDefaultParameters()
    {
        int i;

        svm_parameter param = new svm_parameter();
        // default values
        param.svm_type = svm_parameter.C_SVC;
        param.kernel_type = svm_parameter.RBF;
        param.degree = 3;
        param.gamma = 0.5; // 0;    // 1/k
        param.coef0 = 0;
        param.nu = 0.5;
        param.cache_size = 100; // 100; //100;
        param.C = 512; // 1;
        param.eps = 1e-3;
        param.p = 0.1;
        param.shrinking = 1;
        param.probability = 0;
        param.nr_weight = 0;
        param.weight_label = new int[0];
        param.weight = new double[0];
        return param;
    }

    public static boolean fromYesNo(String s)
    {
        if ("yes".equalsIgnoreCase(s))
            return true;
        if ("no".equalsIgnoreCase(s))
            return false;
        throw new IllegalArgumentException("String must be yes or no not " + s);
    }

    public static boolean from0or1(String s)
    {
        if ("0".equalsIgnoreCase(s))
            return true;
        if ("1".equalsIgnoreCase(s))
            return false;
        throw new IllegalArgumentException("String must be yes or no not " + s);
    }

    public static String[] readinLines(String file)
    {
        List<String> holder = new ArrayList<String>();

        try {
            LineNumberReader rdr = new LineNumberReader(new FileReader(file));
            String line = rdr.readLine();
            while (line != null) {
                holder.add(line);
                line = rdr.readLine();
            }
            String[] ret = new String[holder.size()];
            holder.toArray(ret);
            return ret;
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public static void saveDataSource(ISVMDataSource src,String fileName)
    {
        try {
            PrintWriter pw = new PrintWriter(new FileWriter(fileName));
            ISVMDataItem[] items = src.getDataItems();
            for (int i = 0; i < items.length; i++) {
                ISVMDataItem item = items[i];
                pw.println(item.toString());
            }
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
